# @Version        : 1.0
# @Update Time    : 2025/11/9 23:15
# @File           : dept_service.py
# @IDE            : PyCharm
# @Desc           : 文件描述信息
from typing import Optional, List

from fastapi import HTTPException
from starlette import status
from tortoise.expressions import Q

from applications.models import Dept


class DeptService:
    """部门业务逻辑"""

    @staticmethod
    async def update_ancestors(dept: Dept):
        """
        更新部门的 ancestors 字段
        ancestors 不包含自己，使用 / 分隔，格式: "/1/2/"
        """
        if dept.parent_id and dept.parent_id != 0:
            parent = await Dept.get_or_none(id=dept.parent_id)
            if not parent:
                raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"父部门不存在: {dept.parent_id}")

            # 检查是否会形成循环引用
            if await DeptService.is_descendant(parent, dept.id):
                raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="不能将部门移动到其子部门下")

            # 拼接祖先: 父部门的ancestors + 父部门ID + /
            # 例如: 父部门 ancestors="/1/", id=2 => 当前部门 ancestors="/1/2/"
            if parent.ancestors:
                dept.ancestors = f"{parent.ancestors}{parent.id}/"
            else:
                dept.ancestors = f"/{parent.id}/"
        else:
            # 顶级部门，ancestors 为空
            dept.ancestors = ""

        await dept.save()

    @staticmethod
    async def is_descendant(dept: Dept, ancestor_id: int) -> bool:
        """判断某个部门是否是指定部门的子孙"""
        if not dept.ancestors:
            return False
        # ancestors="/1/2/", 判断是否包含 "/2/"
        ancestor_ids = [int(x) for x in dept.ancestors.strip("/").split("/") if x]
        return ancestor_id in ancestor_ids

    @staticmethod
    async def update_children_ancestors(dept_id: int):
        """递归更新所有子部门的 ancestors"""
        children = await Dept.filter(parent_id=dept_id).all()
        for child in children:
            await DeptService.update_ancestors(child)
            # 递归更新子部门的子部门
            await DeptService.update_children_ancestors(child.id)

    @staticmethod
    async def get_dept_with_children(dept_id: int) -> List[int]:
        """
        获取部门及其所有子孙部门的ID列表
        使用前缀匹配，可以利用索引
        """
        dept_ids = [dept_id]

        # 方式1: 使用前缀匹配（推荐，可以用索引）
        # 查询 ancestors 以 "/dept_id/" 开头的所有部门
        children = await Dept.filter(
            Q(ancestors__startswith=f"/{dept_id}/") | Q(ancestors=f"/{dept_id}/")  # 兼容只有一个父级的情况
        ).all()
        dept_ids.extend([child.id for child in children])

        # 方式2: 使用 LIKE 匹配
        # children = await Dept.filter(
        #     ancestors__contains=f"/{dept_id}/"
        # ).all()

        return dept_ids

    @staticmethod
    async def build_dept_tree(depts: List[Dept], parent_id: int = 0) -> List[dict]:
        """构建部门树形结构"""
        tree = []
        for dept in depts:
            if dept.parent_id == parent_id:
                dept_dict = {
                    "id": dept.id,
                    "parent_id": dept.parent_id,
                    "ancestors": dept.ancestors,
                    "dept_name": dept.dept_name,
                    "sort": dept.sort,
                    "leader": dept.leader,
                    "phone": dept.phone,
                    "email": dept.email,
                    "status": dept.status,
                    "remark": dept.remark,
                    "address": dept.address,
                    "create_at": dept.create_at.strftime("%Y-%m-%d %H:%M:%S"),
                    "update_at": dept.update_at.strftime("%Y-%m-%d %H:%M:%S"),
                    "children": await DeptService.build_dept_tree(depts, dept.id),
                }
                tree.append(dept_dict)

        # 按 sort 排序
        tree.sort(key=lambda x: x["sort"])
        return tree

    @staticmethod
    async def validate_dept_name(dept_name: str, parent_id: int, exclude_id: Optional[int] = None) -> bool:
        """验证部门名称在同级是否重复"""
        query = Dept.filter(dept_name=dept_name, parent_id=parent_id)
        if exclude_id:
            query = query.exclude(id=exclude_id)

        exists = await query.exists()
        if exists:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST, detail=f"同级部门下已存在名称为 '{dept_name}' 的部门"
            )
        return True
